/**
 * LookupServiceRegistry.java
 */
package fss.common;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.List;

import fss.core.config.FssConfigFactory;
import fss.core.config.ServiceRegistryConfig;
import fss.remote.ServiceRegistry;
import fss.remote.util.RemoteFileService;
import fss.util.rmi.RmiId;
import fss.util.rmi.RmiUtils;
import java.rmi.Remote;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * This provides the capability to search for available Service Registry
 * according to the configuration file.
 * 
 * @author thinh
 * 
 */
public class LookupServiceRegistry {
    /**
     * Return the id of the 'best' available Service Registry.
     * <p>
     * The logic will pick the registry with the least number of registered services 
     * where their registered service(s) contains the least number of total activities.
     * @return the id.
     */
    public static RmiId lookup() {
        RmiId bestId = null;
        Map<RmiId, Set<RmiId>>  registrationMap = getAllServiceRegistryRegistration();
        int min = Integer.MAX_VALUE;
        for(RmiId _id : registrationMap.keySet()) {
            Set<RmiId> registration = registrationMap.get(_id);
            // registration has the least number of connected service(s)
            if(registration.size() < min) {
                bestId = _id;
                min = registration.size();
            } 
            // compare the activities of the connected service(s) 
            else if(registration.size() == min) {
                // find out the total number of activities for all connected services for current
                int totalActivities = 0;
                for(RmiId info : registration) {
                    try {
                        totalActivities += ((RemoteFileService)RmiUtils.getRemoteObject(info)).getStatus().size();
                    } catch (RemoteException e) {
                        // ignore
                    }
                }
                
                // find out the total number of activities for all connected services for the already set min
                int minTotalActivities = 0;
                for(RmiId info : registrationMap.get(bestId)) {
                    try {
                        minTotalActivities += ((RemoteFileService)RmiUtils.getRemoteObject(info)).getStatus().size();
                    } catch (RemoteException e) {
                        // ignore
                    }
                }
                
                if(totalActivities < minTotalActivities) {
                    bestId = _id;
                    min = registration.size();
                }
            }
        }
        return bestId;
    }
    /**
     * Get a set of all Service Registry in the <b>ENTIRE</b> network as defined by the 
     * configuration file.
     * @param ignore a list of remote identifiers to ignore.
     * @return
     */
    public static Map<RmiId, Set<RmiId>> getAllServiceRegistry(List<RmiId> ignore) {
        ServiceRegistryConfig config = FssConfigFactory.getInstance().getRegistryServerConfig();
        List<Integer> portList = config.getPortList();
        List<String> hostList = config.getHostList();
        Map<RmiId, Set<RmiId>> all = new HashMap<RmiId, Set<RmiId>>();
        
        for (String host : hostList) {
            for (int port : portList) {
                try {
                    Registry registry = LocateRegistry.getRegistry(host, port);
                    for(String name : registry.list()) {
                        RmiId _id = new RmiId(host, name, port);
                        Remote remoteObject = RmiUtils.getRemoteObject(_id);
                        if(remoteObject instanceof ServiceRegistry) {
                            ServiceRegistry serviceReg = (ServiceRegistry) remoteObject;
                            if(ignore != null) {
                                try {
                                    // only add registration if it is not in the ignore list
                                    if(!ignore.contains(_id)) {
                                        all.put(serviceReg.getId(), serviceReg.getRegisteredServiceList());
                                    }
                                } catch(RemoteException e) {}
                            } else {
                                all.put(serviceReg.getId(), serviceReg.getRegisteredServiceList());
                            }
                        }
                        
                    }
                } catch (RemoteException e) {
                    // ignore, no service registry at host/port
                }
            }
        }
        return all;
    }
    /**
     * Obtain a map of the registration list of all available Service Registries.
     * <p>
     * This iterates through the config and find all the possible service registries,
     * and can be very, very, very long depending on how many hosts are set the the
     * configuration file.
     * @return a map of serice registry IDs to their list of registered service(s).
     */
    public static Map<RmiId, Set<RmiId>> getAllServiceRegistryRegistration() {
        Map<RmiId, Set<RmiId>> all = getAllServiceRegistry(null);
        return all;
    }

  /**
   * Iterate through all host and port in the configuration to find all
   * available Remote Objects, and return the list of found RmiId
   * @return List of RmiId
   */
  public static ArrayList<RmiId> getAvailableRegistryList()
  {
    ArrayList<RmiId> tFoundList = new ArrayList<RmiId>();
    ServiceRegistryConfig tConfig =
      FssConfigFactory.getInstance().getRegistryServerConfig();
    for (String host : tConfig.getHostList())
    {
      for (int port : tConfig.getPortList())
      {
        try
        {
          HashMap<String, String> tFoundRemote =
            RmiUtils.listRmiRegistryRemoteObjects(host, port);

          for (String tName : tFoundRemote.keySet())
          {
            RmiId tId = new RmiId(host, tName, port);
            Remote tTest = RmiUtils.getRemoteObject(tId);
            if (tTest instanceof ServiceRegistry)
            {
              tFoundList.add(tId);
            }
          }
        }
        catch (RemoteException aEx)
        {
          //aEx.printStackTrace();
        }
      }
    }
    return tFoundList;
  }
}
